package com.xyh.transaction.aop;

import com.xyh.transaction.common.Current;
import com.xyh.transaction.common.LogLevel;
import com.xyh.transaction.entity.Log;
import com.xyh.transaction.entity.User;
import com.xyh.transaction.entity.dto.user.EditOrAddDto;
import com.xyh.transaction.service.LogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class LoggingAspect {

    @Autowired
    private LogService logService;

    // 定义切点，匹配所有服务类的操作
    @Pointcut("execution(* com.xyh.transaction.service.*Service.*(..)) && !execution(* com.xyh.transaction.service.LogService.*(..))")
    private void servicePointcut() {
    }


    @Around("servicePointcut()")
    public Object serviceLogAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String serviceName = getServiceName(proceedingJoinPoint);
//        log.info("开始记录 {} 日志！", serviceName);
        LogLevel logLevel = LogLevel.INFO;
        String exceptInfo = null;

        Object result = null;
        try {
            // 执行目标方法
            result = proceedingJoinPoint.proceed();
        } catch (Exception e) {
            // TODO 区分异常，设置日志等级？？
            logLevel = LogLevel.ERROR;
            exceptInfo = e.toString();
            throw e;
        } finally {
            saveLog(proceedingJoinPoint, result, serviceName, logLevel, exceptInfo);
//            log.info("记录 {} 日志结束! ", serviceName);
        }

        return result;
    }

    private String getServiceName(ProceedingJoinPoint proceedingJoinPoint) {
        return proceedingJoinPoint.getTarget().getClass().getSimpleName();
    }

    private void saveLog(ProceedingJoinPoint proceedingJoinPoint
            , Object result, String serviceName, LogLevel logLevel, String exceptInfo) {
        // 获取目标方法的参数
        Object[] args = proceedingJoinPoint.getArgs();

        // 目标方法名
        String methodName = proceedingJoinPoint.getSignature().getName();

        // 获取请求信息
        String requestMethod = null;
        String requestURI = null;
        String remoteAddr = null;
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            requestMethod = request.getMethod();
            requestURI = request.getRequestURI();
            remoteAddr = request.getRemoteAddr();
        }

        String username;

        // 根据方法名设置用户名
        if ("login".equals(methodName)) {
            User user = (User) args[0];
            username = user.getUsername();
        } else if ("register".equals(methodName)) {
            EditOrAddDto user = (EditOrAddDto) args[0];
            username = user.getUsername();
        } else {
            User user = Current.getCurrentUser().getUser();
            username = user.getUsername();
        }

        Log logEntity = new Log();
        logEntity.setLogLevel(logLevel);
        logEntity.setTimestamp(LocalDateTime.now());

        // 设置日志消息
        logEntity.setLogMessage(
                "操作: " + methodName + ", " +
                        "用户名: " + username + ", " +
                        "结果: " + (String.valueOf(result).length() > 100 ?
                        result.toString().substring(0, 100) + " ..." :
                        String.valueOf(result)));

        logEntity.setLogSource(serviceName + "中方法：" + methodName);
        logEntity.setUsername(username);
        logEntity.setInputParameters(Arrays.toString(args));
        logEntity.setOperationResult(String.valueOf(result).length() > 255 ?
                String.valueOf(result).substring(0, 255) + "..." :
                String.valueOf(result));
        logEntity.setExceptionInfo(exceptInfo);
        logEntity.setIpAddress(remoteAddr);
        logEntity.setRequestMethod(requestMethod);
        logEntity.setRequestUrl(requestURI);

//        log.info(logEntity.toString());
        logService.save(logEntity);
    }

}
